/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.grid.util;

import org.apache.commons.beanutils.PropertyUtils;

import java.util.Comparator;

/**
 * <p>Class used to compare fields of entities. Property may be indexed, mapped, or nested.
 * </p>
 * <p><a href="EntityFieldComparator.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.3 $ $Date: 2006/08/03 10:08:21 $
 */
public class EntityFieldComparator implements Comparator {

	// ~ Static variables

	// ~ Instance variables

	/**
	 * Name of field to sort by
	 */
	protected String property;

	/**
	 * Order of sorting by default is always ascending
	 * Can be reversed using <code>reverseOrder()</code> method
	 */
	protected int order = 1;

	/**
	 * Creates new instance of SortField
	 */
	public EntityFieldComparator() {
	}

	/**
	 * Creates new instance of SortField
	 *
	 * @param property Name of property to compare
	 */
	public EntityFieldComparator(String property) {
		this.property = property;
	}

    /**
     * <p>
     * Compares two objects by property. If no <code>property</code> is specified,
     * an attempt is taken to compare them as Comparable. Else firstly value of
     * given property is extracted from each object. Null property is less than
     * non-null one. If both properties are non-null, they are compared as
     * Comparable.
     * </p>
     * <p>
     * Note that if order was reversed, the comparison will work vice-versa
     * </p>
     *
     * @param o1 First object
     * @param o2 Second object
     * @return -1 if first is less then second, +1 if it's more and 0 if equal
     */
	public int compare(Object o1, Object o2) {
		if ( o1 == null ) {
			throw new NullPointerException("First argument must not be null");
		}

		if ( o2 == null ) {
			throw new NullPointerException("Second argument must not be null");
		}

		// if property was not specified, check if objects implement comparable interface
		// if they are, compare them, otherwise return 0 (assume that they are equal, because there is no way to compare them)
		if ( property == null ) {
            if ( o1 instanceof Comparable && o2 instanceof Comparable ) {
	            Comparable c1 = (Comparable) o1;
	            Comparable c2 = (Comparable) o2;
	            return order * c1.compareTo(c2);
            } else {
	            return 0;
            }
		}

		Object p1 = null;
		Object p2 = null;
		try {
			p1 = PropertyUtils.getProperty(o1, property);
		} catch ( Exception e ) {
			throw new IllegalArgumentException("Exception " + e.getClass().getName() + " with message '" + e.getMessage() + "' while trying to obtain value of property '" + property + "' from instance of " + o1.getClass().getName());
		}

		try {
			p2 = PropertyUtils.getProperty(o2, property);
		} catch ( Exception e ) {
			throw new IllegalArgumentException("Exception " + e.getClass().getName() + " with message '" + e.getMessage() + "' while trying to obtain value of property '" + property + "' from instance of " + o2.getClass().getName());
		}

		// assume, that null is less than any object which is not null
		if ( p1 == null && p2 == null ) {
			return 0;
		} else if ( p1 == null && p2 != null ) {
			return -1 * order;
		} else if ( p1 != null & p2 == null ) {
			return order;
		}

		if ( !(p1 instanceof Comparable) ) {
			throw new ClassCastException("Value of property '" + property + "' must implement java.lang.Comparable interface");
		}

		Comparable c1 = (Comparable) p1;
		Comparable c2 = (Comparable) p2;

		return order * (c1.compareTo(c2));
	}

    /**
     * Reverses the order of comparing
     *
     */
	public void reverseOrder() {
		order = -order;
	}

}
